<?php


namespace App\Traits;

use Illuminate\Support\Facades\Redis;
trait RedisOperation
{
    /**
     * 设置缓存
     * @param string $key 缓存key值
     * @param mixed $value 键值，支持数组、对象
     * @param int $expire 过期时间(秒)
     * @return bool 返回布尔值
     */
    public static function set($key, $value, $expire = 0)
    {
        if (empty($key) || empty($value)) {
            return false;
        }

        $value = is_int($value) ? $value : serialize($value);

        if ($expire == 0) {
            // 秒为单位的到期值
            return Redis::set($key, $value);

        }else {
            // 不设置过期时间
            return self::_setex($key, $value, (int)$expire);
        }

    }

    /**
     * 将 key 的值设为 value,当且仅当 key不存在
     * @param  string or int $key  键名
     * @param  mixed  $value 要指定的健值，支持数组
     * @param  int $expire 过期时间，如果不填则默认永久有效
     * @return bool  设置成功返回true 否则false
     */
    public static function setnx($key, $value, $expire = 0)
    {
        if (empty($key) || empty($value)) {
            return false;
        }
        $value = is_int($value) ? $value : serialize($value);

        $res = Redis::setnx($key,$value);
        //设置过期时间
        if ($res && $expire != 0) {
            Redis::expire($key, $expire);
        }

        return $res;
    }

    /**
     * 获取缓存
     * @param string or int $key 缓存标识
     * @return mixed
     * */
    public static function get($key)
    {
        if (empty($key)) {
            return false;
        }

        $value = Redis::get($key);
        if (is_object($value)) {
            return $value;
        }

        return is_numeric($value) ? $value : unserialize($value);
    }

    /**
     * 删除指定缓存
     * @param string or int $key
     * @return bool|int
     */
    public static function del($key)
    {
        if (empty($key)) {
            return false;
        }

        return Redis::del($key);
    }

    /**
     * 删除哈希
     * @param $key
     * @return bool
     *
     */
    public static function hDel($table,$column)
    {
        if((int)Redis::hexists($table, $column)){
            Redis::hDel($table,$column);
            return true;

        }
        return false;
    }

    /**
     * 判断缓存是否在 Redis 内
     * @param string or int $key 缓存标识
     * @return int 返回存在个数
     * */
    public static function exists($key)
    {
        if (empty($key)) {
            return false;
        }

        return Redis::exists($key);
    }

    /**
     * 将 key 中储存的数字值自增
     * @param  string or int $key  键名
     * @param  int $int 自增量，如果不填则默认是自增量为 1
     * @return int  返回自增后的值，如果键不存在则新创建一个值为0，并在此基础上自增，返回自增后的数值.如果键值不是可转换的整数，则返回false
     */
    public static function inc($key, $int = 0)
    {
        if ((int) $int) {
            return Redis::incrby($key, $int);
        }else {
            return Redis::incr($key);
        }
    }

    /**
     * 将 key 中储存的数字值自减
     * @param  string or int $key  键名
     * @param  int $int 自减量，如果不填则默认是自减量为 1
     * @return int  返回自减后的值，如果键不存在则新创建一个值为0，并在此基础上自减，返回自减后的数值.如果键值不是可转换的整数，则返回false
     */
    public static function dec($key, $int = 0)
    {
        if ((int) $int) {
            return Redis::decrby($key, $int);
        }else {
            return Redis::decr($key);
        }
    }

    /**
     * 设置以秒为过期时间单位的缓存
     * @param string $key 缓存标识
     * @param mixed  $value 缓存的数据
     * @param int    $time 缓存过期时间
     * @return bool  设置成功返回true 否则false
     * */
    private static function _setex($key, $value, $time)
    {
        return Redis::setex($key, $time, $value);
    }

    /**
     * 为哈希表中的字段赋值
     * @param  string  $table  哈希表名
     * @param  string  $column 字段名
     * @param  string|array  $value  字段值
     * @param  int $expire 过期时间, 如果不填则不设置过期时间
     * @return int  如果成功返回 1，否则返回 0.当字段值已存在时覆盖旧值并且返回 0
     */
    public static function hset($table, $column, $value, $expire=0)
    {
        $value = is_array($value) ? json_encode($value) : $value;
        $res = Redis::hset($table, $column, $value);
        if((int)$expire){
            Redis::expire($table, $expire);
        }
        return $res;
    }

    /**
     * 获取哈希表字段值
     * @param  string $table  表名
     * @param  string $column 字段名
     * @return mixed  返回字段值，如果字段值是数组保存的返回json格式字符串，转换成数组json_encode($value),如果字段不存在返回false;
     */
    public static function hget($table, $column)
    {
        return Redis::hget($table, $column);
    }

    /**
     * 返回哈希表中，所有的字段和值
     * @param  string $table 表名
     * @return array   返回键值对数组
     */
    public static function hgetall($table)
    {
        return Redis::hgetall($table);
    }

    /**
     * 查看哈希表的指定字段是否存在
     * @param  string $table  表名
     * @param  string $column 字段名
     * @return bool  存在返回true,否则false
     */
    public static function hexists($table, $column)
    {
        if((int)Redis::hexists($table, $column)){
            return true;
        }
        return false;
    }

    /**
     * 为哈希表中不存在的的字段赋值
     * @param  string  $table  哈希表名
     * @param  string  $column 字段名
     * @param  string|array  $value  字段值
     * @param  int $expire 过期时间, 如果不填则不设置过期时间
     * @return bool  如果成功返回true，否则返回 false.
     */
    public static function hsetnx($table, $column, $value, $expire = 0)
    {
        if (is_array($value)) {
            $value = json_encode($value);
        }
        $res = Redis::hsetnx($table, $column, $value);
        if ((int) $expire) {
            Redis::expire($table, $expire);
        }

        return $res;
    }

    /**
     * 为哈希表中的字段值加上指定增量值(支持整数和浮点数)
     * @param  string $table  表名
     * @param  string $column 字段名
     * @param  int $num  增量值，默认1, 也可以是负数值,相当于对指定字段进行减法操作
     * @return int|float|bool  返回计算后的字段值,如果字段值不是数字值则返回false,如果哈希表不存在或字段不存在返回false
     */
    public static function hincrby($table, $column, $num)
    {
        $value = Redis::hexists($table, $column);
        if (is_numeric($value)) {
            $res = Redis::hincrby($table, $column, $num);
            return $res;
        }else {
            return false;
        }
    }

    /**
     * 获取哈希表中的所有字段
     * @param  string $table  表名
     * @return array  返回包含所有字段的数组
     */
    public static function hkeys($table)
    {
        return Redis::hkeys($table);
    }

    /**
     * 返回哈希表所有域(field)的值
     * @param  string $table  表名
     * @return array  返回包含所有字段值的数组,数字索引
     */
    public static function hvals($table)
    {
        return Redis::hvals($table);
    }

    /**
     * 获取哈希表中字段的数量
     * @param  string $table  表名
     * @return int 如果哈希表不存在则返回0
     */
    public static function hlen($table)
    {
        return Redis::hlen($table);
    }

    /**
     * 将一个或多个值插入到列表头部或列表尾部。如果列表不存在，则创建新列表并插入值
     * @param  string  $list  列表名
     * @param  string|array  $value  要插入的值,如果是多个值请放入数组传入
     * @param  string $pop 要插入的位置，默认first头部,last表示尾部
     * @param  int $expire 过期时间, 如果不填则不设置过期时间
     * @return int  返回列表的长度
     */
    public static function lpush($list, $value, $pop = 'first', $expire = false)
    {
        if (is_array($value)) {
            foreach ($value as $v) {
                $res = ($pop =='last') ? Redis::rpush($list, $v) : Redis::lpush($list, $v);
            }
        }else {
            $res = ($pop =='last') ? Redis::rpush($list, $value) : Redis::lpush($list, $value);
        }

        if ((int)$expire) {
            Redis::expire($list, $expire);
        }

        return $res;
    }

    /**
     * 移出并获取列表的第一个元素或最后一个元素
     * @param  string $list 列表名
     * @param  string $pop 移出并获取的位置，默认first为第一个元素
     * @return string|bool  列表第一个元素或最后一个元素,如果列表不存在则返回false
     */
    public static function lpop($list, $pop = 'first')
    {
        if ($pop == 'last') {
            return Redis::rpop($list);
        }
        return Redis::lpop($list);
    }

    /**
     * 返回列表的长度
     * @param  string $list 列表名
     * @return int  列表长度
     */
    public static function llen($list)
    {
        return Redis::llen($list);
    }

    /**
     * 返回列表中指定区间内的元素
     * @param  string  $list  列表名
     * @param  int  $start  起始位置，从0开始计,默认0
     * @param  int  $end 结束位置，-1表示最后一个元素，默认-1
     * @return array  返回列表元素数组
     */
    public static function lrange($list, $start=0, $end=-1)
    {
        return Redis::lrange($list, $start, $end);
    }

    /**
     * 移除列表中指定的元素
     * @param  string  $list  列表名
     * @param  string  $element  指定的元素
     * @param  int $count  要删除的个数，0表示删除所有指定元素，负整数表示从表尾搜索, 默认0
     * @return int  被移除元素的数量。 列表不存在时返回 0
     */
    public static function lrem($list, $element, $count=0)
    {
        return Redis::lrem($list, $count, $element);
    }

    /**
     * 通过索引获取列表中的元素
     * @param  string  $list  列表名
     * @param  int  $index  索引位置，从0开始计,默认0表示第一个元素，-1表示最后一个元素索引
     * @return string  返回指定索引位置的元素
     */
    public static function lindex($list, $index=0)
    {
        return Redis::lindex($list, $index);
    }

    /**
     * 通过索引来设置元素的值
     * @param  string $list  列表名
     * @param  string $value 元素值
     * @param  int $index 索引值
     * @return bool  成功返回true,否则false.当索引参数超出范围，或列表不存在返回false。
     */
    public static function lset($list, $index, $value)
    {
        return Redis::lset($list, $index, $value);
    }

    /**
     * 让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除
     * @param  string $list  列表名
     * @param  int $start 起始位置，从0开始
     * @param  int $stop  结束位置，负数表示倒数第n个
     * @return bool  成功返回true否则false
     */
    public static function ltrim($list, $start, $stop)
    {
        return Redis::ltrim($list, $start, $stop);
    }

    /**
     * 用于在指定的列表元素前或者后插入元素。如果元素有重复则选择第一个出现的。当指定元素不存在于列表中时，不执行任何操作
     * @param  string $list   列表名
     * @param  string $element 指定的元素
     * @param  string $value   要插入的元素
     * @param  string $pop     要插入的位置，before前,after后。默认before
     * @return int  返回列表的长度。 如果没有找到指定元素 ，返回 -1 。 如果列表不存在或为空列表，返回 0 。
     */
    public static function linsert($list, $element, $value, $pop = 'before')
    {
        return Redis::linsert($list, $pop, $element, $value);
    }

    /**
     * 从列表中弹出最后一个值，将弹出的元素插入到另外一个列表开头并返回这个元素
     * @param  string $sourceList  要弹出元素的列表名
     * @param  string $destinationList  要接收元素的列表名
     * @return string|bool  返回被弹出的元素,如果其中有一个列表不存在则返回false
     */
    public static function brpoplpush($sourceList, $destinationList)
    {
        if(self::lrange($sourceList) && self::lrange($destinationList)){
            return Redis::brpoplpush($sourceList, $destinationList, 500);
        }
        return false;
    }

    /**
     * 给当前集合添加一个元素
     * 如果value已经存在，会更新score的值。
     * @param string $key
     * @param string $score 序号
     * @param string $value 值
     * @return bool
     */
    public static function zadd($keyName,$score = 1,$value)
    {
        return Redis::zadd($keyName,$score,$value);
    }

    /**
     * 集合以order递增排列后，0表示第一个元素，-1表示最后一个元素
     * @param string $key
     * @param int $start
     * @param int $end
     * @return array|bool
     */
    public static function zrange($keyName,$start,$end)
    {
        return Redis::zrange($keyName,$start,$end);
    }

    /**
     * 集合以order递减排列后，0表示第一个元素，-1表示最后一个元素
     * @param string $key
     * @param int $start
     * @param int $end
     * @return array|bool
     */
    public static function zrevrange($keyName,$start,$end,$withScore = false)
    {
        if($withScore)
        {
            return Redis::zrevrange($keyName,$start,$end,'WITHSCORES');
        }else{
            return Redis::zrevrange($keyName,$start,$end);
        }
    }

    public static function zincrby($keyName,int $increment = 1,$member)
    {
        return Redis::zincrby($keyName,$increment,$member);
    }

    public static function sMembers($key)
    {
        if (empty($key)) {
            return false;
        }

        $value = Redis::sMembers($key);
        if (is_array($value)) {
            return $value;
        }

        return [];
    }

    /**
     * 添加集合。由于版本问题，扩展不支持批量添加。这里做了封装
     * @param unknown $key
     * @param string|array $value
     */
    public static function sAdd($key,$value, $expire = 0)
    {
        if(!is_array($value))
            $arr=array($value);
        else
            $arr=$value;
        foreach($arr as $row)
            Redis::sAdd($key,$row);
        if ($expire) {
            Redis::expire($key, $expire);
        }
        return true;
    }

    /**
     * 返回集合元素数量，不存在返回0
     * @param $key
     * @return bool
     */
    public static function sCard($key)
    {
        if (empty($key)) {
            return false;
        }

        return Redis::sCard($key);
    }
}